package net.esj.tree.service.impl;

import java.io.Serializable;
import java.lang.reflect.ParameterizedType;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;

import javax.annotation.PostConstruct;
import javax.annotation.Resource;

import net.esj.basic.service.impl.DefaultServiceImpl;
import net.esj.basic.utils.Pagination;
import net.esj.tree.service.TreeService;
import net.esj.tree.utils.TreeEntity;

import org.springframework.context.MessageSource;

public abstract class TreeServiceImpl<T> extends DefaultServiceImpl implements TreeService<T> {

	@Resource
	private MessageSource messageSource;
	
	private String DEFAULT_TREE_ROOT_TEXT_KEY = "default.tree.root.text";
	
	static final TreeEntity DEFAULT_ROOT = new TreeEntity();
	
	@PostConstruct
	void initService(){
		DEFAULT_ROOT.setId(DEFAULT_TREE_ROOT_ID_LONG);
		DEFAULT_ROOT.setText(messageSource.getMessage(DEFAULT_TREE_ROOT_TEXT_KEY, null, Locale.getDefault()));
	}

	@Override
	public List<TreeEntity> getChildren(Serializable parentId, boolean hasChild) {
		List<T> list = findChildren(parentId);
		List<TreeEntity> reval = new ArrayList<TreeEntity>();
		for(T t:list){
			TreeEntity entity = transTreeEntity(t);
			reval.add(entity);
		}
		if(hasChild){
			for(TreeEntity entity:reval){
				entity.setChildren(getChildren(entity.getId(), hasChild));
			}
		}
		return reval;
	}

	@Override
	public List<TreeEntity> getWholeTree(boolean needRoot) {
		List<T> list = getAll();
		List<T> templist = new ArrayList<T>(list);
		List<TreeEntity> reval = new ArrayList<TreeEntity>();
		
		if(needRoot){
			TreeEntity root = getRoot();
			reval.add(root);
			appendChild(templist, root);
		}else{
			for(T t : list){
				TreeEntity e = transTreeEntity(t);
				if(isRoot(e)){
					reval.add(e);
					templist.remove(t);
				}
			}
			for(TreeEntity tree:reval){
				appendChild(templist, tree);
			}
		}
		
		return reval;
	}
	
	@Override
	public Pagination<TreeEntity> getChildrenPage(Serializable parentId,Pagination page) {
		page = findChildrenPage(parentId, page);
		List<TreeEntity> list = new ArrayList<TreeEntity>();
		for(Object  o: page.getRows()){
			T t = (T) o;
			TreeEntity entity = transTreeEntity(t);
			list.add(entity);
		}
		page.setRows(list);
		return page;
	}

	protected void  appendChild(List<T> list,TreeEntity tree){
		List<T> reval = new ArrayList<T>(list);
		for(T t : reval){
			if(isChild(tree, t)){
				tree.getChildren().add(transTreeEntity(t));
				list.remove(t);
			}
		}
		if(tree.getChildren().size()>0){
			for(TreeEntity child: tree.getChildren()){
				appendChild(list, child);
			}
		}
	}

	protected List<T> getAll(){
		return findAll(getGeneralClass());
	}
	
	protected abstract TreeEntity transTreeEntity(T  t);
	
	protected abstract boolean isChild(TreeEntity parent, T t);
	
	protected abstract List<T> findChildren(Serializable parentId);
	
	protected abstract Pagination<T> findChildrenPage(Serializable parentId,Pagination page);
	
	protected boolean isRoot(TreeEntity entity){
		return entity == DEFAULT_ROOT;
	}
	
	public Class<T> getGeneralClass(){
		 return (Class<T>) ((ParameterizedType)getClass().getGenericSuperclass()).getActualTypeArguments()[0];
	}
	
	protected TreeEntity getRoot(){
		return DEFAULT_ROOT;
	}

	public void setMessageSource(MessageSource messageSource) {
		this.messageSource = messageSource;
	}
	
}
